#ifndef DATAMEMORYORGANISATION_HPP
#define DATAMEMORYORGANISATION_HPP

#include <math.h>

#include <QObject>
#include <QDebug>
#include <QMutex>
#include <QMutexLocker>

#include "bank.hpp"
#include "ioregister.hpp"
#include "register.hpp"
#include "pix.hpp"

class Pix;

/**
* @brief Class DataMemoryOrganisation provides access to the underlying Banks and Registers
* @details This class provides several functions to interact with the data storage (Registers) \n
* It holds the currently selected Bank, switches the Bank automatically and provides additional \n
* access to the not selected Bank (decision will be made according to the Register's address)
*
* @class DataMemoryOrganisation datamemoryorganisation.hpp "datamemoryorganisation.hpp"
*/
class DataMemoryOrganisation : public QObject
{
Q_OBJECT
public:
    /**
    * @brief Constructor for DataMemoryOrganisation
    *
    * @fn explicit DataMemoryOrganisation(QObject *parent = 0)
    * @param pix
    * @param parent
    */
    explicit DataMemoryOrganisation(Pix *pix, QObject *parent = 0);

    /**
    * @brief Copy Constructor for DataMemoryOrganisation
    *
    * @fn DataMemoryOrganisation(const DataMemoryOrganisation &other)
    * @param other
    */
    DataMemoryOrganisation(const DataMemoryOrganisation &other);

    /**
    * @brief DataMemoryOrganisation's Destructor
    *
    * @fn ~DataMemoryOrganisation
    */
    ~DataMemoryOrganisation();

    /**
    * @brief Switches the bank between 0 and 1
    *
    * @fn switchBank
    */
    void switchBank();

    /**
    * @brief Switches the bank to the given one
    * @details false = bank 0, true = bank 1
    *
    * @fn void switchBank(bool bank)
    * @param bank
    */
    void switchBank(bool bank);

    /**
    * @brief Returns the actual bank
    *
    * @fn Bank *getActualBank() const
    * @return Bank
    */
    Bank *getActualBank();

    /**
    * @brief Returns the bank depending on the bank parameter.
    *
    * @fn Bank *getBank(bool bank) const
    * @param bank
    * @return Bank
    */
    Bank *getBank(bool bank) const;

    /**
    * @brief Returns the register for PortA
    *
    * @fn IORegister *getPortA()
    * @return IORegister
    */
    IORegister *getPortA();

    /**
    * @brief Returns the register for PortB
    *
    * @fn IORegister *getPortB()
    * @return IORegister
    */
    IORegister *getPortB();

    /**
    * @brief Return the working register
    *
    * @fn Register *getWorkingRegister() const
    * @return Register
    */
    Register *getWorkingRegister() const;

    /**
    * @brief Returns the prescaler register
    *
    * @fn Register *getPrescaler()
    * @return Register
    */
    Register *getPrescaler();

    /**
    * @brief Returns the register depending onthe address
    *
    * @fn Register *getRegister(int address)
    * @param address
    * @return Register
    */
    Register *getRegister(int address);

    /**
    * @brief Resets both banks, the working register and the prescaler register
    *
    * @fn void reset()
    */
    void reset();

    /**
    * @brief
    *
    * @fn void resetNormalOperation()
    */
    void resetNormalOperation();

private:
    /**
    * @brief Indicates the actual bank
    * @details false = bank 0, true = bank 1
    *
    * @var actualBank
    */
    //bool actualBank;
    QMutex *mutex;
    Bank *bank0;
    Bank *bank1;
    IORegister *portA;
    IORegister *portB;
    Register *workingRegister;
    Register *prescaler;
    Register *timer;
    Pix *pix;

    /**
    * @brief Initiates the registers for bank0 and bank1
    *
    * @fn void initRegister()
    */
    void initRegister();

signals:

    /**
    * @brief Emitted when bank switches
    *
    * @fn void switchBankSignal(bool bank)
    */
    void switchBankSignal(bool bank);

    /**
    * @brief Emitted when register changes
    *
    * @fn void registerChanged(Register *reg)
    * @param reg
    */
    void registerChanged(Register *reg);

    /**
    * @brief Emitted on interrupt
    *
    * @fn void interrupt()
    */
    void interrupt();

public slots:
    /**
    * @brief Slot called when the content of any of the connected Registers has changed
    *
    * @fn void onContentChanged(int oldValue)
    * @param oldValue
    */
    void onContentChanged(int oldValue);

    /**
    * @brief Slot called when another Class (GUI / HardwareInterface) wants to change a Register
    *
    * @fn void onRequestChangeRegister(int address, int value)
    * @param address
    * @param value
    */
    void onRequestChangeRegister(int address, int value);

    /**
    * @brief Slot called when a Bank switch has been triggered
    *
    * @fn void onRequestSwitchBank(bool bank)
    * @param bank
    */
    void onRequestSwitchBank(bool bank);

};

#endif // DATAMEMORYORGANISATION_HPP
